package com.xucia.jsponic.remote;

import java.io.IOException;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONException;
import org.json.JSONObject;

import com.xucia.jsponic.remote.ClientConnection.PostResponse;

/**
 * 
 * @author Kris Zyp
 *JsponicServlet provides a base servlet class to build servlets that are JSPON aware. By being
 *JSPON aware, servlets may pass receive objects that are primitives or identifiable (instead of just strings) as parameters and output
 *objects (instead of just string text).  It is generally simpler to implement the 
 *JsponicAction class instead of this class, but implementing this class provides more flexibility.
 *Implementors of this class should implement the service method. The service method provides the 
 *RequestHelper that gives access to the parameters, request, and response.
 */
public abstract class JsponicServlet extends HttpServlet{
	public abstract class Action {
		public void setHeader(String name, String value) {
			if (headerTarget instanceof HttpServletResponse)
				((HttpServletResponse)headerTarget).setHeader(name,value);
			else if (headerTarget instanceof JSONObject) {
				try {
					((JSONObject)headerTarget).put(name, value);
				} catch (JSONException e) {
					throw new RuntimeException(e);
				}
			}
			else throw new RuntimeException("Unacceptable header target");
		}
		Object headerTarget;
		void setHeaderTarget(Object target) {
			headerTarget = target;
		}
		public boolean isCallback() {
			return headerTarget instanceof JSONObject;
		}
		public abstract void doAction(Writer writer) throws Exception;
	}
	/**
	 * This method should be implemented to handle the action of the servlet
	 * @param requestHelper provides the information about the request and response
	 */
	protected void doPost(final RequestHelper requestHelper){
		throw new UnsupportedOperationException("post not implemented");
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	RequestHelper requestHelper = new RequestHelper(request,response);
    	doPost(requestHelper);
	}
	/**
	 * This method should be implemented to handle the action of the servlet
	 * @param requestHelper provides the information about the request and response
	 */
	protected void doGet(final RequestHelper requestHelper) {
		throw new UnsupportedOperationException("get not implemented");
	}

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	RequestHelper requestHelper = new RequestHelper(request,response);
    	doGet(requestHelper);
	}
	/**
	 * This method handles this situation where a cross-browser request is made, and the response must be 
	 * delivered as normal output or as a JSON-P response or as a hash to an iframe proxy. 
	 * @param request
	 * @param response
	 * @param action
	 */
	public static void handleOutputMethods(HttpServletRequest request, HttpServletResponse response, Action action) {
		try {
			String postResponseId = request.getParameter("PostResponse"); 
			boolean doingRedirectedPost = postResponseId != null && request.getMethod().equalsIgnoreCase("POST"); 
	        Writer out;
	        JSONObject suffixHeader = null;
	        PostResponse postResponse = null;
	        String suffixString = null;
			//System.err.println("Doing post response " + postResponseId + " post part " + doingRedirectedPost); 
			if (doingRedirectedPost) {
				response.setContentType("text/html");
				postResponse = ClientConnection.getCurrentObjectResponse().newPostResponse(postResponseId);
				out = postResponse.writer;
				action.setHeaderTarget(suffixHeader = postResponse.headers);
				Writer realWriter = response.getWriter();
				realWriter.write("<html><body>Response will be in the subsequent get with PostResponse=" + postResponseId + "</body></html>");
				realWriter.close();
			}
			else if (request.getParameter("jsonp") != null) {
				action.setHeaderTarget(suffixHeader = new JSONObject());
				out = response.getWriter();
				if (request.getPathInfo().endsWith(".js"))
					suffixString = "\n" + request.getParameter("jsonp") + "(temp";
				else
					out.write(request.getParameter("jsonp") + "(");
	    	}
			else {
				out = response.getWriter();
				action.setHeaderTarget(response);
			}
	        //response.setCharacterEncoding("UTF16");
	        //System.err.println("parameters " + parameters);

	    	if (suffixHeader != null) {
	            DateFormat formatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss zzz");
	            formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
	            try {
		            suffixHeader.put("Date", formatter.format(new Date()));
				} catch (JSONException e) {
					throw new RuntimeException(e);
				}
	    	}
	    	if (postResponseId != null && !doingRedirectedPost) { // we are delivering the post response then
	    		postResponse = ClientConnection.getCurrentObjectResponse().pullPostResponse(postResponseId);
	    		out.write(postResponse.writer.toString());
	    		suffixHeader = postResponse.headers;
	    	}
	    	else
		    	try {
		    		action.doAction(out);
		    	}
		    	catch (Throwable e) {
		            e.printStackTrace();
		            while (e.getCause() != null && e.getCause() != e)
		            	e = e.getCause();
		            String reason = e.getMessage();
		            if (reason == null)
		            	out.write(" new Error(\"" + e.getClass() +  "\")");
		            else
		            	out.write(" new Error(" + JSONObject.quote(reason) +  ")"); 
		    	}
		    if (suffixString != null)
		    	out.write(suffixString);
	    	if (suffixHeader != null && !doingRedirectedPost)
	    		out.write("," + suffixHeader + ");");
			out.close();
			if (doingRedirectedPost)
				postResponse.finished = true;
/*	    	if (asHash) {
	    		Writer realWriter = response.getWriter();
	    		String responseText = out.toString();
	    		if (responseText.length() > 2000)
	    			throw new RuntimeException("cross-domain iframe proxy hash based responses are not capable of handling over 2000 bytes at this point");
	    		realWriter.write("<html><script>window.location.hash=" + JSONObject.quote("{data:" + out.toString() + ",headers:" + suffixHeader.toString() + "}") + ";</script><body>cross-domain response</body></html>");
	    	}*/
		}
		catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
}
